#include "elswitch.h"
#include <QPaintEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include "eltheme.h"

using std::get;
using std::make_tuple;

const int ElSwitch::Init_Width = 40;
const int ElSwitch::Init_Height = 22;

ElSwitch::ElSwitch(QWidget *parent)
    : ElComponent{parent}
{
    resize(Init_Width, Init_Height);
    setCursor(Qt::PointingHandCursor);
    setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    _inactive_state = make_tuple(ThemeColor::lineColor,
                                 QString(),
                                 ThemeColor::textPrimaryColor,
                                 QVariant());
    _active_state = make_tuple(ThemeColor::primaryColor,
                               QString(),
                               ThemeColor::primaryColor,
                               QVariant());
    switchCircle = new ElSwitchCircle(this);
    initPalette();
}

void ElSwitch::setWidth(int w)
{
    resize(w, height());
}

void ElSwitch::setText(State state, const QString &text)
{
    bool isActive = (state == Active);
    get<1>(isActive ? _active_state : _inactive_state) = text;
    update();
}

QString ElSwitch::text(State state) const
{
    bool isActive = (state == Active);
    return get<1>(isActive ? _active_state : _inactive_state);
}

void ElSwitch::setForeground(State state, const QColor &fgColor)
{
    bool isActive = (state == Active);
    get<2>(isActive ? _active_state : _inactive_state) = fgColor;
    initPalette(); // refresh when setting _active_state or _inactive_state
    update();
}

QColor ElSwitch::foreground(State state) const
{
    bool isActive = (state == Active);
    return get<2>(isActive ? _active_state : _inactive_state);
}

void ElSwitch::setBackground(State state, const QColor &bgColor)
{
    bool isActive = (state == Active);
    get<0>(isActive ? _active_state : _inactive_state) = bgColor;
    initPalette(); // refresh when setting the bg or fg of _active_state or _inactive_state
    update();
}

QColor ElSwitch::background(State state) const
{
    bool isActive = (state == Active);
    return get<0>(isActive ? _active_state : _inactive_state);
}

void ElSwitch::setData(State state, const QVariant &data)
{
    bool isActive = (state == Active);
    get<3>(isActive ? _active_state : _inactive_state) = data;
}

QVariant ElSwitch::data(State state) const
{
    bool isActive = (state == Active);
    return get<3>(isActive ? _active_state : _inactive_state);
}

ElSwitch::State ElSwitch::state() const
{
    return _state;
}

void ElSwitch::setState(State state)
{
    if (_state == state)
        return;
    _state = state;
    _palette.toggleColorGroup(isActive() ? ElPalette::Normal : ElPalette::Inactive);
    switchCircle->update();
    update();
    emit stateChanged(_state);
}

bool ElSwitch::event(QEvent *e)
{
    if (e->type() == QEvent::EnabledChange) {
        auto cur_cg = isActive() ? ElPalette::Normal : ElPalette::Inactive;
        _palette.toggleColorGroup(isEnabled() ? cur_cg : ElPalette::Disabled);
        update();
    }
    return ElComponent::event(e);
}

void ElSwitch::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);

    // get bounding rects for the text on both sides
    qreal extra_left = 0, extra_right = 0, spacing = 8; // extra width for text on both sides
    auto inactive_text = get<1>(_inactive_state), active_text = get<1>(_active_state);
    if (!inactive_text.isEmpty()) {
        QRectF br;
        painter.drawText(QRectF(), Qt::AlignCenter, inactive_text, &br);
        extra_left = br.width();
    }
    if (!active_text.isEmpty()) {
        QRectF br;
        painter.drawText(QRectF(), Qt::AlignCenter, active_text, &br);
        extra_right = br.width();
    }

    // resize to fit the text
    setWidth(Init_Width + extra_left + extra_right + 2 * spacing);
    // draw rounded rect
    auto bgColor = isActive()    ? _palette.color(ElPalette::Background)
                   : isEnabled() ? _palette.color(ElPalette::Inactive, ElPalette::Background)
                                 : _disabled_inactive_colors[0];
    painter.setPen(bgColor);
    painter.setBrush(bgColor);
    drawing_rect = QRectF(extra_left + spacing, 0, Init_Width, height());
    if (switchCircle->pos() == QPoint()) {
        auto offset_x = isActive() ? drawing_rect.right() - switchCircle->width() - 2
                                   : drawing_rect.left() + 2;
        switchCircle->move(offset_x, 2);
    }
    // qDebug() << drawing_rect;
    painter.drawRoundedRect(drawing_rect, height() / 2.0, height() / 2.0);

    // draw text on both sides
    if (isEnabled()) {
        // left side
        painter.setPen(_palette.color(isActive() ? ElPalette::Inactive : ElPalette::Normal,
                                      ElPalette::Foreground));
        painter.drawText(QRectF(0, 0, extra_left, height()), Qt::AlignCenter, inactive_text);
        // right side
        painter.setPen(_palette.color(isActive() ? ElPalette::Normal : ElPalette::Inactive,
                                      ElPalette::Foreground));
        painter.drawText(QRectF(width() - extra_right, 0, extra_right, height()),
                         Qt::AlignCenter,
                         active_text);
    } else {
        // currentColorGroup is Disabled when !isEnabled(), so colorGroup is the same.
        // then colorRole decides which color to be painted.
        auto dis_active_fg_color = _palette.color(ElPalette::Disabled, ElPalette::Foreground);
        // left side
        painter.setPen(isActive() ? _disabled_inactive_colors[1] : dis_active_fg_color);
        painter.drawText(QRectF(0, 0, extra_left, height()), Qt::AlignCenter, inactive_text);
        // right side
        painter.setPen(isActive() ? dis_active_fg_color : _disabled_inactive_colors[1]);
        painter.drawText(QRectF(width() - extra_right, 0, extra_right, height()),
                         Qt::AlignCenter,
                         active_text);
    }
    e->accept();
}

void ElSwitch::mouseReleaseEvent(QMouseEvent *e)
{
    if (!isEnabled()) {
        e->ignore();
        return;
    }
    auto pos = e->pos();
    if (pos.x() >= 0 && pos.x() <= width() && pos.y() >= 0 && pos.y() <= height()) {
        update();
        toggle();
    }
    e->accept();
}

void ElSwitch::initPalette()
{
    _palette.setColor(ElPalette::Normal, ElPalette::Background, get<0>(_active_state));
    _palette.setColor(ElPalette::Normal, ElPalette::Foreground, get<2>(_active_state));
    _palette.setColor(ElPalette::Inactive, ElPalette::Background, get<0>(_inactive_state));
    _palette.setColor(ElPalette::Inactive, ElPalette::Foreground, get<2>(_inactive_state));
    _palette.setColor(ElPalette::Disabled,
                      ElPalette::Background,
                      getDisabledColor(get<0>(_active_state)));
    QColor fgColor = get<2>(_active_state), disFgColor;
    if (fgColor == ThemeColor::textPrimaryColor)
        disFgColor = ThemeColor::textDisabledColor;
    else
        disFgColor = getDisabledColor(fgColor);
    _palette.setColor(ElPalette::Disabled, ElPalette::Foreground, disFgColor);
    _disabled_inactive_colors[0] = getDisabledColor(get<0>(_inactive_state));
    fgColor = get<2>(_inactive_state);
    if (fgColor == ThemeColor::textPrimaryColor)
        disFgColor = ThemeColor::textDisabledColor;
    else
        disFgColor = getDisabledColor(fgColor);
    _disabled_inactive_colors[1] = disFgColor;
    _palette.toggleColorGroup(isActive() ? ElPalette::Normal : ElPalette::Inactive);
}

QColor ElSwitch::getDisabledColor(const QColor &color)
{
    return get<2>(ElTheme::generateTriColor(color));
}

void ElSwitch::toggle()
{
    _palette.toggleColorGroup(isActive() ? ElPalette::Inactive : ElPalette::Normal);
    _state = isActive() ? Inactive : Active;
    switchCircle->update();
    switchCircle->slide();
    emit toggled(isActive());
    emit stateChanged(_state);
}

const qreal ElSwitch::ElSwitchCircle::Radius = ElSwitch::Init_Height / 2.0 - 3;

ElSwitch::ElSwitchCircle::ElSwitchCircle(ElSwitch *parent)
    : ElComponent{parent}
{
    resize(2.2 * Radius, 2.2 * Radius);
    setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    _anim = new QPropertyAnimation(this, "pos", this);
    _anim->setDuration(200);
    _anim->setEasingCurve(QEasingCurve::OutQuad);
}

void ElSwitch::ElSwitchCircle::slide()
{
    auto parent = dynamic_cast<ElSwitch *>(this->parent());
    QRectF r = parent->drawing_rect;
    auto offset_x = parent->isActive() ? r.right() - width() - 2 : r.left() + 2;
    _anim->setStartValue(pos());
    move(offset_x, 2);
    _anim->setEndValue(pos());
    _anim->start();
}

void ElSwitch::ElSwitchCircle::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    painter.setPen(Qt::white);
    painter.setBrush(Qt::white);
    // if parent is active, circle should be on right side.
    QRectF _rectf(this->rect());
    painter.drawEllipse(_rectf.center(), Radius, Radius);
    e->accept();
}

void ElSwitch::ElSwitchCircle::initPalette()
{ /*no implement*/
}
